package com.swg.myspring.util;

import com.swg.myspring.bean.BeanDefinition;
import com.swg.myspring.bean.BeanDefinition2;
import com.swg.myspring.bean.PropertyDefinition;
import org.apache.commons.beanutils.ConvertUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

public class SwgClassPathXmlApplicationContext2 {
    private List<BeanDefinition2> beanDefines=new ArrayList<>();//用来存储所有的beans
    private Map<String, Object> sigletons =new HashMap<>();//用来存储实例化后的bean

    public SwgClassPathXmlApplicationContext2(String fileName){
        this.readXml(fileName);
        this.instanceBeans();
        //3.实现对依赖对象的注入功能
        this.injectObject();
    }

    private void injectObject() {
        for(BeanDefinition2 beanDefinition:beanDefines){
            //先拿到一个beanDefinition，比如我先拿到personService2
            Object bean = sigletons.get(beanDefinition.getId());
            if (bean != null) {
                try {
                    //通过类Introspector的getBeanInfo方法获取对象的BeanInfo信息
                    BeanInfo info = Introspector.getBeanInfo(bean.getClass());
                    //通过BeanInfo来获取属性的描述器(PropertyDescriptor),通过这个属性描述器就可以获取某个属性对应的getter/setter方法,然后我们就可以通过反射机制来调用这些方法。
                    //获得bean所有的属性描述
                    PropertyDescriptor[] pds = info.getPropertyDescriptors();
                    //遍历要注入的bean的所有属性,就是<property>元素所有的注入的值或者对象，没有<property>元素则直接跳过
                    for (PropertyDefinition propertyDefinition : beanDefinition.getPropertyDefinitions()) {
                        //拿到一个<property>元素后，遍历这个bean下的所有属性值，看是否有一样的，有的话则说明要将数据注入进这个属性中
                        for (PropertyDescriptor propertyDescriptor : pds) {
                            //比如我这里的propertyDefinition恰好拿到的是<property name="personDao" ref="personDao"/>
                            //因为我们的bean恰好是personService2，那么这个类中的有两个私有属性：private PersonDao personDao;private Integer age;
                            //判断personDao和<property name="personDao" ref="personDao"/>中的name是否相等，这里显然是相等的，那么就要进行注入了，就是调用setter方法
                            if (propertyDefinition.getName().equals(propertyDescriptor.getName())) {
                                //获取用于写入属性值的setter方法
                                Method setter = propertyDescriptor.getWriteMethod();
                                //如果确实有setter方法
                                if (setter != null) {
                                    //用来存储引用的值
                                    Object value=null;
                                    //这里是判断是否注入的是对象，如果是对象，那么就有ref属性，否则是普通的value
                                    if (propertyDefinition.getRef() != null && !propertyDefinition.getRef().equals("")) {
                                        //拿到personDao实例对象，先存在value中
                                        value=sigletons.get(propertyDefinition.getRef());
                                    }else {
                                        //普通的值需要转换成对象，比如这里的24转为Integer类型的value，先存到Object变量中
                                        value= ConvertUtils.convert(propertyDefinition.getValue(), propertyDescriptor.getPropertyType());
                                    }
                                    setter.setAccessible(true);//保证setter方法可以访问私有
                                    try {
                                        //执行写入属性值的方法
                                        setter.invoke(bean, value);//把引用对象注入到属性，这里就是将personDao注入到PersonService里了
                                    } catch (Exception e) {
                                        System.out.println("注入失败...");
                                    }
                                }
                                break;//找到了注入的属性后，跳出循环
                            }
                        }
                    }
                }catch (Exception e){
                    System.out.println("依赖注入失败...");
                }
            }
        }
    }

    private void readXml(String fileName) {
        //创建一个读取器
        SAXReader saxReader=new SAXReader();
        Document document=null;
        try {
            //获取要读取的配置文件的路径
            URL xmlPath=this.getClass().getClassLoader().getResource(fileName);
            //读取文件内容
            document=saxReader.read(xmlPath);
            //1.获取xml中的根元素
            Element rootElement=document.getRootElement();
            for (Iterator iterator = rootElement.elementIterator(); iterator.hasNext();) {
                Element element = (Element) iterator.next();
                String id=element.attributeValue("id");//获取bean的id属性值
                String clazz=element.attributeValue("class");//获取bean的class属性值
                BeanDefinition2 beanDefinition=new BeanDefinition2(id,clazz);
                //2.获取bean的Property属性
                for (Iterator subElementIterator = element.elementIterator(); subElementIterator.hasNext();) {
                    Element subElement = (Element) subElementIterator.next();
                    String propertyName=subElement.attributeValue("name");
                    String propertyRef= subElement.attributeValue("ref");
                    String propertyValue=subElement.attributeValue("value");
                    //3.构造一个propertyDefinition对象,并且添加进List属性中
                    PropertyDefinition propertyDefinition=new PropertyDefinition(propertyName, propertyRef,propertyValue);
                    beanDefinition.getPropertyDefinitions().add(propertyDefinition);
                }
                beanDefines.add(beanDefinition);
            }
        } catch (Exception e) {
            System.out.println("解析xml失败");
        }
    }

    //这里实例化的功能只是实例化PersonDaoImpl和PersonServiceImpl两个对象，注入的关系还需要下面的函数来解决
    private void instanceBeans(){
        if(beanDefines!=null && beanDefines.size()>0){
            for(BeanDefinition2 beanDefinition:beanDefines){
                try {
                    if (beanDefinition.getClassName() != null && !beanDefinition.getClassName().equals("")) {
                        sigletons.put(beanDefinition.getId(),Class.forName(beanDefinition.getClassName()).newInstance());
                        System.out.println("id为："+beanDefinition.getId()+"的bean实例化成功");
                    }
                }catch (Exception e){
                    System.out.println("bean实例化失败");
                }
            }
        }
    }

    public Object getBean(String beanName,Class beanClass){
        return sigletons.get(beanName);
    }



}
